package utils

import (
	"bytes"
	"encoding/json"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"
)

type RequestClient struct {
	Client  *http.Client
	BaseURL *url.URL
}

func NewRequestClient(baseURL string) (*RequestClient, error) {
	parsedURL, err := url.Parse(baseURL)
	if err != nil {
		return nil, err
	}
	return &RequestClient{
		Client: &http.Client{
			Timeout: 10 * time.Second,
		},
		BaseURL: parsedURL,
	}, nil
}

func (c *RequestClient) Get(path string) (*http.Response, error) {
	u, err := c.BaseURL.Parse(path)
	if err != nil {
		return nil, err
	}
	return c.Client.Get(u.String())
}

func (c *RequestClient) Post(path string, bodyType string, body interface{}) (*http.Response, error) {
	u, err := c.BaseURL.Parse(path)
	if err != nil {
		return nil, err
	}
	var buf io.Reader
	if body != nil {
		switch v := body.(type) {
		case io.Reader:
			buf = v
		case string:
			buf = bytes.NewBufferString(v)
		case []byte:
			buf = bytes.NewBuffer(v)
		default:
			b, err := json.Marshal(v)
			if err != nil {
				return nil, err
			}
			buf = bytes.NewBuffer(b)
		}
	}
	req, err := http.NewRequest("POST", u.String(), buf)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", bodyType)
	return c.Client.Do(req)
}

func Get(url string, result interface{}) error {
	return _doRequest("GET", url, nil, nil, result)
}

func Post(url string, body io.Reader, header map[string]string, result interface{}) error {
	return _doRequest("GET", url, nil, nil, result)
}

func _doRequest(method, url string, body io.Reader, header map[string]string, result interface{}, timeout ...time.Duration) error {
	client := http.DefaultClient

	if len(timeout) > 0 {
		client = &http.Client{Timeout: timeout[0], Transport: client.Transport}
	}

	req, err := http.NewRequest(method, url, body)
	if err != nil {
		return err
	}

	if body != nil {
		req.Header.Set("Content-Type", "application/json")
	}

	if header != nil {
		for k, s := range header {
			req.Header.Set(k, s)
		}
	}

	response, err := client.Do(req)
	if err != nil {
		return err
	}

	defer Close(response.Body)

	data, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return err
	}

	err = json.Unmarshal(data, result)
	if err != nil {
		return err
	}

	return nil
}
